(** 
    Mlib.ml
    A general library for the project. You will find i/o operations functions,
    global variables and useful functions. See mlib.mli for more details.
    Bozman Çagdas <bozman.cagdas@gmail.com> - 2011
    Razakamiadana Tefin <tefy.razaka@gmail.com>   - 2011
*)

(* Set the flag to true if you want to activate the debug mode *)
let debug_flag = false

let debug fmt = Printf.eprintf fmt

module Set = Set.Make (
  struct 
    type t = String.t
    let compare = String.compare
  end)

let spf = Printf.sprintf
      
let from_group s index = 
  try (Str.matched_group index s) with Invalid_argument _ -> ""

let page = Str.regexp ".*</\\(page\\|siteinfo\\)>"
let title = Str.regexp ".*<title>\\([^\\]*\\)</title>.*"
(*let link = Str.regexp ".*\\[\\[\\([^/|]+\\).*\\]\\].*"*)
let link = Str.regexp ".*\\[\\[\\(.*\\)\\]\\].*"

let read in_ch = 
  try Some (input_line in_ch) with End_of_file -> None

let read_value in_ch =
  try Some (input_value in_ch) with End_of_file -> None
  
let read_page in_ch =
  let rec aux accu_str title_str =
    match read in_ch with
      | None -> title_str, accu_str
      | Some line ->
	  if (Str.string_match page line 0) then title_str, accu_str ^ line
	  else if (Str.string_match title line 0) then
	    aux (accu_str ^ line) (from_group line 1)
	  else aux (accu_str ^ line) title_str
  in
    match aux "" "" with
      | "", "" -> None,   None
      | t, ""  -> Some t, None
      | "", s  -> None,   Some s 
      | t, s   -> Some t, Some s

let write = output_string

let write_value = output_value

let fr_wiki = "/info/master2/bozman/M2/prog_comparee/projet/frwiki-latest-pages-articles.xml"

let offsets_list = "/info/master2/bozman/M2/prog_comparee/projet/src/offsets_list_save"

let cwiki = "/info/master2/bozman/M2/prog_comparee/projet/src/wiki_compressed"

let open_in_file input_filename = 
  try open_in input_filename 
  with Sys_error _ -> failwith (spf "Cannot open the file %s" input_filename)

let open_out_file output_filename = 
  try open_out output_filename 
  with Sys_error _ -> failwith (spf "Cannot open the file %s" output_filename)

let list_of_page_offsets from_file = 
  let file = open_in_file from_file in
  let rec aux accu =
    match read file with
      | None      ->  let _ = close_in file in List.rev (List.tl accu)
      | Some line -> 
	  if (Str.string_match page line 0) then 
	    aux ((pos_in file) :: accu)
	  else aux accu
  in
  aux []

let create_offsets_list wiki_file =
  let l = list_of_page_offsets wiki_file in
  let o_file = open_out_file offsets_list in
    output_value o_file l;
    close_out o_file
      
let list_to_queue l = 
  let rec aux q = function
    | [] -> q
    | x :: xs -> let _ = (Queue.add x q) in aux q xs
  in
    aux (Queue.create ()) l
    
let compact_string =
  fun l ->
    let rec aux cpt accu l =
      match l with
	| [] -> List.tl (List.rev accu)
	| x :: xs ->
	    if Str.string_match link x 0 then
	      let accu = (from_group x 1) :: (string_of_int cpt) :: accu in
		aux 1 accu xs (* Starts again the counter *)
	    else
	      aux (cpt + 1) accu xs
    in
      aux 1 [] l


(* TOUT CE QUI EST EN DESSOUS EST NOUVEAU *)

let nb_words s = 
  let delim = Str.regexp "[ ]" in
  let l = Str.split delim s in
    List.length l

let get_sresult = function
  | Str.Text s -> s
  | Str.Delim s -> s      

let get_link = 
  let parse_link link = 
    let regexp = Str.regexp "\\(.*\\)|.*" in
      if Str.string_match regexp link 0 then
	from_group link 1
      else link
  in
    function
      | x1 :: x2 :: xs ->
	  if (get_sresult x2) = "]]"
	  then (parse_link (get_sresult x1), xs)
	  else 
	    begin
	      match xs with
		| x3 :: x4 :: x5 :: xss ->
		    (parse_link (get_sresult x3), xss)
		| _ -> "", [] (* failwith "Link not closed or empty." *)
	    end
      | _ -> "", [](* assert false *) (* XXCAGO c'est quoi ce cas ??*)

	      
let rec compact res = function
  | [] -> List.rev res
  | x :: xs ->
      begin
	match x with 
	  | Str.Delim _ ->
	      let link, nxt = get_link xs in
		compact (link :: res) nxt
	  | _ ->
	      let nb = string_of_int (nb_words (get_sresult x)) in
		compact (nb :: res) xs
      end

let partition n l =
  let cpt = ref 0 in
  let ql = ref [] in
  let rec aux accu = function
    | [] -> ql := (accu :: !ql); List.rev !ql
    | x :: xs ->
	if !cpt = n then 
	  begin
	    cpt := 0;
	    ql := (List.rev accu) :: !ql;
	    aux [x] xs
	  end
	else
	  begin
	    incr cpt;
	    aux (x :: accu) xs
	  end
  in
    aux [] l 
